library(tidyverse)
library(infer)
library(mosaic)
library(caret)
library(ggplot2)
library(mdsr)
library(rpart)
library(rpart.plot)
library(partykit)
library(randomForest)
library(pROC)
library(gbm)
library(Metrics)
library(viridis)
require(maps)
require(viridis)
theme_set(
theme_void()
)
library(knitr)
library(tmap)
library(ggpubr)
The Premise
Hello! This project is the final for my STAT228: Introduction to Data
Science course at Simmons University. With this project, I’d like to
encompass everything I’ve learned throughout the semester, as well as
some additional data science methods that I have taught myself outside
of class. (The primary non-curriculum methods I am using come from the
package tmap, which I was made aware of from a LinkedIn
post by Joachim Schork, a data science educator & consultant from
Germany). The premise of my project is to predict & analyze Women’s
Empowerment Index scores for countries ; in this project, I aim to find
the best version of the model predicting WEI scores using a variety of
ensemble methods. There are two datasets I’m interested in using
here:
I’d like to join the two datasets by the common variable “Country”,
and analyze WEI scores by health factors related to the patient’s
country. I am interested in creating several maps that will visualize
WEI scores against other health-based factors.
Cleaning the Data
I want to start by filtering led to only include data where
the year = 2015, because this is the most recent year within this
dataset.
led_2015 <- led%>%
filter(Year=="2015")
Now, I want to rename several datapoints within the led_2015
dataset because I want the data names to be congruent between
led_2015 and wei. This will be very tedious, but it is
necessary to be able to join the datasets!
led_2015[led_2015$Country == "Bolivia(PlurinationalStateof)", "Country"] <- "Bolivia"
wei[wei$Country == "Bolivia(Plurinational State of)", "Country"] <- "Bolivia"
led_2015[led_2015$Country == "BosniaandHerzegovina", "Country"] <- "Bosnia and Herzegovina"
led_2015[led_2015$Country == "BurkinaFaso", "Country"] <- "Burkina Faso"
wei[wei$Country == "Congo (Democratic Republic of the)", "Country"] <- "DemocraticRepublicoftheCongo"
led_2015[led_2015$Country == "CostaRica", "Country"] <- "Costa Rica"
led_2015[led_2015$Country == "DominicanRepublic", "Country"] <- "Dominican Republic"
led_2015[led_2015$Country == "ElSalvador", "Country"] <- "El Salvador"
led_2015[led_2015$Country == "Iran(IslamicRepublicof)", "Country"] <- "Iran (Islamic Republic of)"
led_2015[led_2015$Country == "LaoPeople'sDemocraticRepublic", "Country"] <- "Laos"
wei[wei$Country == "Lao People's Democratic Republic", "Country"] <- "Laos"
led_2015[led_2015$Country == "RepublicofMoldova", "Country"] <- "Moldova (Republic of)"
led_2015[led_2015$Country == "SierraLeone", "Country"] <- "Sierra Leone"
led_2015[led_2015$Country == "SouthAfrica", "Country"] <- "South Africa"
led_2015[led_2015$Country == "SriLanka", "Country"] <- "Sri Lanka"
led_2015[led_2015$Country == "TheformerYugoslavrepublicofMacedonia", "Country"] <- "North Macedonia"
led_2015[led_2015$Country == "UnitedRepublicofTanzania", "Country"] <- "Tanzania (United Republic of)"
wei[wei$Country == "Türkiye", "Country"] <- "Turkey"
led_2015[led_2015$Country == "UnitedArabEmirates", "Country"] <- "United Arab Emirates"
led_2015[led_2015$Country == "UnitedKingdomofGreatBritainandNorthernIreland", "Country"] <- "United Kingdom"
led_2015[led_2015$Country == "UnitedStatesofAmerica", "Country"] <- "United States"
led_2015[led_2015$Country == "VietNam", "Country"] <- "Viet Nam"
That took a WHILE! But, now my two datasets should be better matched,
and it’ll be much easier to join them!
Now I’m going to remove some unnecessary variables from the datasets
This is because some variables have too many missing datapoints to be
useful, or because some variables may be redundant.
led_2015 = subset(led_2015, select = -c(Alcohol, Totalexpenditure,Year))
Now, I will be joining the two datasets using inner_join, because I
only want the countries in common between both dataframes. I’m also
going to create a second version that is full_joined, just in case I end
up needing it!
dataset <- wei%>% inner_join(led_2015,by="Country")
dataset2 <- wei%>% full_join(led_2015,by="Country")
dim(dataset)
## [1] 112 25
Finally, I will be renaming a few variables because I dislike having
spaces in my variable names. After this, I will have finished cleaning
the data! I’m also removing a few more variables just because they seem
rather redundant - for example, the Gender Parity Group and Gender
Parity Index variables may be too similar to WEI, therefore they might
have skewed levels of correlation.
dataset <- dataset %>%
rename(WEI = "Women's Empowerment Index (WEI) - 2022")
dataset <- dataset %>%
rename(WEG = "Women's Empowerment Group - 2022")
dataset <- dataset %>%
rename(GGPI = "Global Gender Parity Index (GGPI) - 2022")
dataset <- dataset %>%
rename(HDG = "Human Development Group - 2021")
dataset <- dataset %>%
rename(GPG = "Gender Parity Group - 2022")
dataset <- dataset %>%
rename(SDG_Region = "Sustainable Development Goal regions")
dataset = subset(dataset, select = -c(GPG, SDG_Region,GGPI))
All finished cleaning! Now I want to take a peek at the dataset I’ll
be working with before I go into the next steps.
dim(dataset)
## [1] 112 22
summary(dataset$WEI)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.1410 0.5180 0.6150 0.6085 0.7070 0.8280
tally(dataset$Status)
## X
## Developed Developing
## 31 81
Intial Visualization
Now, I want to create a map of WEI by country, just to get a general
visual of how the distribution looks.
I’m going to start out by creating a joined dataset between
dataset and World, so I can have a mappable dataset
using the tmap package.
library(tmap)
data("World")
dataset<- dataset %>%
rename(name = Country)
wei.map <- inner_join(dataset,World,by="name")
Unfortunately, the wei.map only contains 98 individuals
despite dataset containing 112 individuals! This means that 14
individuals have disharmonious names between dataset and
World. I’m going to run some code renaming a whole bunch of
countries, just like I did above with the led_2015 dataset. I’m
going to hide this code, just because it’s super tedious work. Tidying
& cleaning your data is a constant process!
dataset[dataset$name == "Bosnia and Herzegovina", "name"] <- "Bosnia and Herz."
dataset[dataset$name == "DemocraticRepublicoftheCongo", "name"] <- "Dem. Rep. Congo"
dataset[dataset$name == "Dominican Republic", "name"] <- "Dominican Rep."
dataset[dataset$name == "Iran (Islamic Republic of)", "name"] <- "Iran"
dataset[dataset$name == "Laos", "name"] <- "Lao PDR"
dataset[dataset$name == "North Macedonia", "name"] <- "Macedonia"
dataset[dataset$name == "Moldova (Republic of)", "name"] <- "Moldova"
dataset[dataset$name == "Tanzania (United Republic of)", "name"] <- "Tanzania"
dataset[dataset$name == "Viet Nam", "name"] <- "Vietnam"
Now, let’s try re-joining these datasets!
wei.map <- right_join(dataset,World,by="name")
library(s2)
library(sf)
## Warning: package 'sf' was built under R version 4.3.2
wei.map.sf <- wei.map %>%
st_as_sf()
And visualizing:
tmap_mode("plot")
tm_shape(wei.map.sf) +
tm_polygons("WEI",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Women's Empowerment Index Scores", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)
Lovely! I also made an interactive version of the map, just for further
exploration of the tmap package.
tmap_mode("view")
tm_shape(wei.map.sf) +
tm_polygons("WEI",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Women's Empowerment Index Scores", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)
Now, I want to move onto creating models that will help me predict
the WEI scores of countries based on other factors.
Decision Tree
I’m going to start by creating a decision tree for my
dataset. I believe this will be beneficial to see, seeing as
the decision tree will select the best predictors for my response
variable. The first step here is to split my data into training and
testing data.
set.seed(228)
train.index <- createDataPartition(dataset$WEI, p= 0.8, list = FALSE)
train <- dataset[train.index,]
test <- dataset[-train.index,]
I’m going to look at the proportion of developed vs developing
countries within both my test and train data. This is
so I can ensure that test is at least somewhat representative
of the distribution within train.
tally(test$Status, format='proportion')
## X
## Developed Developing
## 0.3 0.7
tally(train$Status, format = 'proportion')
## X
## Developed Developing
## 0.2717391 0.7282609
tally(dataset$Status, format = 'proportion')
## X
## Developed Developing
## 0.2767857 0.7232143
Both test and train appear to be adequately
representative of dataset!
Now, I will create my decision tree. I will fit the decision tree to
train, and I will set nearly every predictor variable (BESIDES
NAME & WEG, for reasons of redundancy) as my X. I am doing this so
that the decision tree can tell me which predictor variables have the
highest influence on my response variable, WEI.
tree <- rpart(WEI~ HDG + Status + Lifeexpectancy + AdultMortality + infantdeaths + HepatitisB + Measles + Polio + Diphtheria + Population + Schooling, data=train, cp=.01)
rpart.plot(tree)

It appears there are three predictors that have the highest
correlation with WEI scores : HDG (Human Development Group), Years of
Schooling, and Adult Mortality Rates. For further exploration, I’m going
to create a Random Forest to find our best model.
Random Forest & RMSE
I’m going to have to not use any variables that have “NA” values to
be able to properly crearte a random forest. This unfortunately means
removing Schooling. However, I don’t want to remove Schooling because
according to the decision tree, it’s one of the most important
predictors! So, I am going to use regression imputation to create some
estimated Schooling values for any of our countries that have Schooling
= NA.
missing <- is.na(dataset$Schooling)
sum(missing)
## [1] 6
which(missing)
## [1] 11 30 31 47 72 105
lm(Schooling~WEI, data=dataset)
##
## Call:
## lm(formula = Schooling ~ WEI, data = dataset)
##
## Coefficients:
## (Intercept) WEI
## 2.574 17.943
dataset$Schooling[11]<-2.574+17.943*(0.707)
dataset$Schooling[30]<-2.574+17.943*(0.778)
dataset$Schooling[31]<-2.574+17.943*(0.752)
dataset$Schooling[47]<-2.574+17.943*(0.686)
dataset$Schooling[72]<-2.574+17.943*(0.399)
dataset$Schooling[105]<-2.574+17.943*(0.510)
Yet another example of never being done with cleaning the data! Now,
I have to recreate my training and testing dataset without these missing
values.
set.seed(228)
train.index <- createDataPartition(dataset$WEI, p= 0.8, list = FALSE)
train <- dataset[train.index,]
test <- dataset[-train.index,]
Then, I can finally create my Random Forest
set.seed(228)
rf_model <-randomForest(WEI ~ HDG + Status + Lifeexpectancy + Schooling + AdultMortality + infantdeaths + Measles + Polio + Diphtheria ,data=train, proximity=TRUE, ntree=1000)
rf_model
##
## Call:
## randomForest(formula = WEI ~ HDG + Status + Lifeexpectancy + Schooling + AdultMortality + infantdeaths + Measles + Polio + Diphtheria, data = train, proximity = TRUE, ntree = 1000)
## Type of random forest: regression
## Number of trees: 1000
## No. of variables tried at each split: 3
##
## Mean of squared residuals: 0.005114937
## % Var explained: 72
The mean of squared residuals is pretty close to 0, which means the
model is good! However, I’m going to also try running a Random Forest
model in which I only use the three best predictor variables as
identified by my Decision Tree. I will then compare the two mean of
squared residuals.
set.seed(228)
rf_model2 <-randomForest(WEI ~ HDG + Schooling + AdultMortality ,data=train, proximity=TRUE, ntree=1000)
rf_model2
##
## Call:
## randomForest(formula = WEI ~ HDG + Schooling + AdultMortality, data = train, proximity = TRUE, ntree = 1000)
## Type of random forest: regression
## Number of trees: 1000
## No. of variables tried at each split: 1
##
## Mean of squared residuals: 0.005898256
## % Var explained: 67.72
The mean of squared residuals is ever so slightly higher for this
model, and the Var explained is slightly lower. I’m going to use an RMSE
test to compare the two models.
Now, I’m going to boost the two models. Before doing so, I am
changing HDG’s class to ordered, and Status’s class to ordered, so they
arecompatible with the boosted model.
train$HDG <- as.ordered(train$HDG)
train$Status <- as.ordered(train$Status)
boost1 <- gbm(WEI ~ HDG + Status + Lifeexpectancy + Schooling + AdultMortality + infantdeaths + Measles + Polio + Diphtheria, data=train,distribution = "gaussian", n.trees = 1000, shrinkage = 0.01, n.minobsinnode=5)
boost2 <- gbm(WEI~ Schooling + HDG + AdultMortality , data = train, distribution = "gaussian", n.trees = 1000, shrinkage = 0.01, n.minobsinnode=5)
Now I will evaluate the two models using predictions & RMSE.
predictions1 <- predict(boost1,newdata=test)
rmse(actual=test$WEI, predicted=predictions1)
## [1] 0.0567137
predictions2 <- predict(boost2, newdata = test)
rmse(actual=test$WEI, predicted=predictions2)
## [1] 0.05870663
Once again, the model with nearly very variable has proved to perform
slightly better than the model with only 3 predictor variables.
However, this difference is so small, and I think it is
worth it to sacrifice this small difference for the sake of keeping our
model SIMPLE! The model still performs very well. So, I’m deciding to
keep the model that only contains the three best predictor
variables!
Final Visualizations
wei.map <- right_join(dataset,World,by="name")
wei.map.sf <- wei.map %>%
st_as_sf()
I’m going to create FOUR maps here: One exactly like my first
visualization that shows WEI scores by country, and then three that
shows the distribution of each of my predictor variables! I will display
all of these maps side by side.
tmap_mode("plot")
tm_shape(wei.map.sf) + tm_polygons("WEI",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Women's Empowerment Index Scores", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

tm_shape(wei.map.sf) + tm_polygons("HDG",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Human Development Groups", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

tm_shape(wei.map.sf) + tm_polygons("Schooling",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Plot of Years of Schooling", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

tm_shape(wei.map.sf) + tm_polygons("AdultMortality",palette="magma",n=8) +
tm_layout(inner.margins = c(.05, .05, .05, .05), sepia.intensity=.01, frame.double.line=1, title = "Adult Mortality per Population of 1000", title.position = c("right", "bottom"), title.fontface="bold",title.fontfamily="Times", legend.title.fontfamily = "Times",legend.title.fontface="bold", title.bg.color="lightsalmon", title.size=1.8, legend.position=c("left","center"), saturation = .9, title.bg.alpha=.7)

These are sort of confusing. For one, the HDG categories are out of
order, which is very hard to read. Secondly, the adult mortality map
should probably have the colors flipped, because lower adult mortality
corresponds to higher WEI.
I’m going to fix them, and insert the png below.
knitr::include_graphics("/Users/jasmineday/Downloads/WEIPLOT.jpg")

And there we have our final visualization! I hope you enjoyed reading
through this project as much as I enjoyed my STAT228 class :)
BONUS: Testing the Prediction Model
After finishing the first draft of my project, I figured I wanted to
add a section in which I take a small handful of countries that are NOT
represented in the wei dataset and use our model to predict the
WEI scores for these nations. I will be using the countries Kazakhstan,
New Zealand, Sudan, Haiti, and Ukraine. I will use led_2015 to
source the HDG, Schooling, and AdultMortality for each of these
countries.
finalmodel <- lm(WEI~HDG+Schooling+AdultMortality,data=dataset)
finalmodel
##
## Call:
## lm(formula = WEI ~ HDG + Schooling + AdultMortality, data = dataset)
##
## Coefficients:
## (Intercept) HDGLow HDGMedium HDGVery high Schooling
## 0.1862461 -0.0749196 -0.0202205 0.0717149 0.0282162
## AdultMortality
## 0.0002026
#Kazakhstan:
#HDG=Very high
#Schooling=15
#AdultMortality=198
Kazakhstan_WEI <- .0186 + .0717 + .0282*15 + .0002*198
#New Zealand
#HDG=Very high
#Schooling=19.2
#AdultMortality=66
NZ_WEI <- .0186 + .0717 + .0282*19.2 + .0002*66
#Sudan
#HDG=Low
#Schooling=7.2
#AdultMortality=225
Sudan_WEI <- .0186 - .0749 + .0282*7.2 + .0002*225
#Haiti
#HDG=Medium
#Schooling=9.1
#AdultMortality=24
Haiti_WEI <- .0186 - .0202 + .0282*9.1 + .0002*24
#Ukraine
#HDG=High
#Schooling=15.3
#AdultMortality=195
Ukraine_WEI <- .0186 + .0282*15.3 + .0002*195
Kazakhstan imputed/predicted WEI score = .553 New Zealand
imputed/predicted WEI score = .645 Sudan imputed/predicted WEI score =
.192 Haiti imputed/predicted WEI score = .260 Ukraine imputed/predicted
WEI score = .489
These don’t feel entirely accurate. For example, Sudan’s imputed WEI
score is just above that of Yemen’s, which is the lowest in the world.
However, it still can be beneficial to see where there may be potential
flaws in using this linear model to predict WEI scores! WEI score data
on all countries is not public, so I am unfortunately unable to
check the actual accuracy of these predictions.
LS0tCnRpdGxlOiAiRmluZGluZyB0aGUgQmVzdCBNb2RlbCBmb3IgV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCBTY29yZXMiCmF1dGhvcjogIkphc21pbmUgRGF5IgpkYXRlOiAiTWF5IDZ0aCAyMDI0IgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3IsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShpbmZlcikKbGlicmFyeShtb3NhaWMpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHJwYXJ0KQpsaWJyYXJ5KHJwYXJ0LnBsb3QpCmxpYnJhcnkocGFydHlraXQpCmxpYnJhcnkocmFuZG9tRm9yZXN0KQpsaWJyYXJ5KHBST0MpCmxpYnJhcnkoZ2JtKQpsaWJyYXJ5KE1ldHJpY3MpCmxpYnJhcnkodmlyaWRpcykKcmVxdWlyZShtYXBzKQpyZXF1aXJlKHZpcmlkaXMpCnRoZW1lX3NldCgKICB0aGVtZV92b2lkKCkKICApCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodG1hcCkKbGlicmFyeShnZ3B1YnIpCgpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQpsaWJyYXJ5KHJlYWRyKQoKd2VpIDwtIHJlYWRfY3N2KCJEQVRBU0VUUy93b21lbl9lbXBvd2VybWVudF9pbmRleC5jc3YiKQoKbGVkIDwtIHJlYWRfY3N2KCJEQVRBU0VUUy9sZWQuY3N2IikKCmBgYAoKCiMgVGhlIFByZW1pc2UKSGVsbG8hIFRoaXMgcHJvamVjdCBpcyB0aGUgZmluYWwgZm9yIG15IFNUQVQyMjg6IEludHJvZHVjdGlvbiB0byBEYXRhIFNjaWVuY2UgY291cnNlIGF0IFNpbW1vbnMgVW5pdmVyc2l0eS4gV2l0aCB0aGlzIHByb2plY3QsIEknZCBsaWtlIHRvIGVuY29tcGFzcyBldmVyeXRoaW5nIEkndmUgbGVhcm5lZCB0aHJvdWdob3V0IHRoZSBzZW1lc3RlciwgYXMgd2VsbCBhcyBzb21lIGFkZGl0aW9uYWwgZGF0YSBzY2llbmNlIG1ldGhvZHMgdGhhdCBJIGhhdmUgdGF1Z2h0IG15c2VsZiBvdXRzaWRlIG9mIGNsYXNzLiAoVGhlIHByaW1hcnkgbm9uLWN1cnJpY3VsdW0gbWV0aG9kcyBJIGFtIHVzaW5nIGNvbWUgZnJvbSB0aGUgcGFja2FnZSAqKnRtYXAqKiwgd2hpY2ggSSB3YXMgbWFkZSBhd2FyZSBvZiBmcm9tIGEgTGlua2VkSW4gcG9zdCBieSBKb2FjaGltIFNjaG9yaywgYSBkYXRhIHNjaWVuY2UgZWR1Y2F0b3IgJiBjb25zdWx0YW50IGZyb20gR2VybWFueSkuIFRoZSBwcmVtaXNlIG9mIG15IHByb2plY3QgaXMgdG8gcHJlZGljdCAmIGFuYWx5emUgV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCBzY29yZXMgZm9yIGNvdW50cmllcyA7IGluIHRoaXMgcHJvamVjdCwgSSBhaW0gdG8gZmluZCB0aGUgYmVzdCB2ZXJzaW9uIG9mIHRoZSBtb2RlbCBwcmVkaWN0aW5nIFdFSSBzY29yZXMgdXNpbmcgYSB2YXJpZXR5IG9mIGVuc2VtYmxlIG1ldGhvZHMuIFRoZXJlIGFyZSB0d28gZGF0YXNldHMgSSdtIGludGVyZXN0ZWQgaW4gdXNpbmcgaGVyZTogCgotIDEuIHRoZSAqd2VpKiBkYXRhc2V0LCB3aGljaCBzdGFuZHMgZm9yIFdvbWVuJ3MgRW1wb3dlcm1lbnQgSW5kZXgsIGlzIHRoZSBmaXJzdCBkYXRhc2V0IEknbSBnb2luZyB0byBiZSB1c2luZy4gSXQgaXMgc291cmNlZCBmcm9tIEh1bWFuIERldmVsb3BtZW50IFJlcG9ydHMsIGFuZCBjYW4gYmUgYWNjZXNzZWQgYXQgdGhlIGxpbmsgYmVsb3cuICgqKmh0dHBzOi8vd3d3LmthZ2dsZS5jb20vZGF0YXNldHMvaWFtc291cmF2YmFuZXJqZWUvd29tZW4tZW1wb3dlcm1lbnQtaW5kZXgqKikuIEl0IGNvbnRhaW5zIGluZm9ybWF0aW9uIG9uIHRoZSBXRUkgc2NvcmVzIGZvciAxMTQgY291bnRyaWVzLiAKLSAyLiBUaGUgc2Vjb25kIGRhdGFzZXQgSSdtIGludGVyZXN0ZWQgaW4gdXNpbmcgaXMgdGhlICpsZWQqIGRhdGFzZXQsIHdoaWNoIHN0YW5kcyBmb3IgTGlmZSBFeHB0ZWN0YW5jeSBEYXRhc2V0LCBhbmQgaXMgc291cmNlZCBmcm9tIHRoZSBXb3JsZCBIZWFsdGggT3JnYW5pemF0aW9uLiBUaGlzIGRhdGFzZXQgY2FuIGJlIGFjY2Vzc2VkIGF0IHRoZSBsaW5rIGJlbG93LiAoKipodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2F1Z3VzdHVzMDQ5OC9saWZlLWV4cGVjdGFuY3ktd2hvL2RhdGEqKikuIFRoaXMgZGF0YXNldCBjb250YWlucyBpbXBvcnRhbnQgaGVhbHRoLWNlbnRlcmVkIGRhdGEgb24gZXZlcnkgY291bnRyeSBpbiB0aGUgd29ybGQuCgpJJ2QgbGlrZSB0byBqb2luIHRoZSB0d28gZGF0YXNldHMgYnkgdGhlIGNvbW1vbiB2YXJpYWJsZSAiQ291bnRyeSIsIGFuZCBhbmFseXplIFdFSSBzY29yZXMgYnkgaGVhbHRoIGZhY3RvcnMgcmVsYXRlZCB0byB0aGUgcGF0aWVudCdzIGNvdW50cnkuIEkgYW0gaW50ZXJlc3RlZCBpbiBjcmVhdGluZyBzZXZlcmFsIG1hcHMgdGhhdCB3aWxsIHZpc3VhbGl6ZSBXRUkgc2NvcmVzIGFnYWluc3Qgb3RoZXIgaGVhbHRoLWJhc2VkIGZhY3RvcnMuIAoKIyBDbGVhbmluZyB0aGUgRGF0YQpJIHdhbnQgdG8gc3RhcnQgYnkgZmlsdGVyaW5nICpsZWQqIHRvIG9ubHkgaW5jbHVkZSBkYXRhIHdoZXJlIHRoZSB5ZWFyID0gMjAxNSwgYmVjYXVzZSB0aGlzIGlzIHRoZSBtb3N0IHJlY2VudCB5ZWFyIHdpdGhpbiB0aGlzIGRhdGFzZXQuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KCmxlZF8yMDE1IDwtIAlsZWQlPiUKCWZpbHRlcihZZWFyPT0iMjAxNSIpCgpgYGAKCk5vdywgSSB3YW50IHRvIHJlbmFtZSBzZXZlcmFsIGRhdGFwb2ludHMgd2l0aGluIHRoZSAqbGVkXzIwMTUqIGRhdGFzZXQgYmVjYXVzZSBJIHdhbnQgdGhlIGRhdGEgbmFtZXMgdG8gYmUgY29uZ3J1ZW50IGJldHdlZW4gKmxlZF8yMDE1KiBhbmQgKndlaSouIFRoaXMgd2lsbCBiZSB2ZXJ5IHRlZGlvdXMsIGJ1dCBpdCBpcyBuZWNlc3NhcnkgdG8gYmUgYWJsZSB0byBqb2luIHRoZSBkYXRhc2V0cyEgCmBgYHtyfQoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiQm9saXZpYShQbHVyaW5hdGlvbmFsU3RhdGVvZikiLCAiQ291bnRyeSJdIDwtICJCb2xpdmlhIgoKd2VpW3dlaSRDb3VudHJ5ID09ICJCb2xpdmlhKFBsdXJpbmF0aW9uYWwgU3RhdGUgb2YpIiwgIkNvdW50cnkiXSA8LSAiQm9saXZpYSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkJvc25pYWFuZEhlcnplZ292aW5hIiwgIkNvdW50cnkiXSA8LSAiQm9zbmlhIGFuZCBIZXJ6ZWdvdmluYSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkJ1cmtpbmFGYXNvIiwgIkNvdW50cnkiXSA8LSAiQnVya2luYSBGYXNvIgoKd2VpW3dlaSRDb3VudHJ5ID09ICJDb25nbyAoRGVtb2NyYXRpYyBSZXB1YmxpYyBvZiB0aGUpIiwgIkNvdW50cnkiXSA8LSAiRGVtb2NyYXRpY1JlcHVibGljb2Z0aGVDb25nbyIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkNvc3RhUmljYSIsICJDb3VudHJ5Il0gPC0gIkNvc3RhIFJpY2EiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJEb21pbmljYW5SZXB1YmxpYyIsICJDb3VudHJ5Il0gPC0gIkRvbWluaWNhbiBSZXB1YmxpYyIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIkVsU2FsdmFkb3IiLCAiQ291bnRyeSJdIDwtICJFbCBTYWx2YWRvciIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIklyYW4oSXNsYW1pY1JlcHVibGljb2YpIiwgIkNvdW50cnkiXSA8LSAiSXJhbiAoSXNsYW1pYyBSZXB1YmxpYyBvZikiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJMYW9QZW9wbGUnc0RlbW9jcmF0aWNSZXB1YmxpYyIsICJDb3VudHJ5Il0gPC0gIkxhb3MiCgp3ZWlbd2VpJENvdW50cnkgPT0gIkxhbyBQZW9wbGUncyBEZW1vY3JhdGljIFJlcHVibGljIiwgIkNvdW50cnkiXSA8LSAiTGFvcyIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIlJlcHVibGljb2ZNb2xkb3ZhIiwgIkNvdW50cnkiXSA8LSAiTW9sZG92YSAoUmVwdWJsaWMgb2YpIgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiU2llcnJhTGVvbmUiLCAiQ291bnRyeSJdIDwtICJTaWVycmEgTGVvbmUiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJTb3V0aEFmcmljYSIsICJDb3VudHJ5Il0gPC0gIlNvdXRoIEFmcmljYSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIlNyaUxhbmthIiwgIkNvdW50cnkiXSA8LSAiU3JpIExhbmthIgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiVGhlZm9ybWVyWXVnb3NsYXZyZXB1YmxpY29mTWFjZWRvbmlhIiwgIkNvdW50cnkiXSA8LSAiTm9ydGggTWFjZWRvbmlhIgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiVW5pdGVkUmVwdWJsaWNvZlRhbnphbmlhIiwgIkNvdW50cnkiXSA8LSAiVGFuemFuaWEgKFVuaXRlZCBSZXB1YmxpYyBvZikiCgp3ZWlbd2VpJENvdW50cnkgPT0gIlTDvHJraXllIiwgIkNvdW50cnkiXSA8LSAiVHVya2V5IgoKbGVkXzIwMTVbbGVkXzIwMTUkQ291bnRyeSA9PSAiVW5pdGVkQXJhYkVtaXJhdGVzIiwgIkNvdW50cnkiXSA8LSAiVW5pdGVkIEFyYWIgRW1pcmF0ZXMiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJVbml0ZWRLaW5nZG9tb2ZHcmVhdEJyaXRhaW5hbmROb3J0aGVybklyZWxhbmQiLCAiQ291bnRyeSJdIDwtICJVbml0ZWQgS2luZ2RvbSIKCmxlZF8yMDE1W2xlZF8yMDE1JENvdW50cnkgPT0gIlVuaXRlZFN0YXRlc29mQW1lcmljYSIsICJDb3VudHJ5Il0gPC0gIlVuaXRlZCBTdGF0ZXMiCgpsZWRfMjAxNVtsZWRfMjAxNSRDb3VudHJ5ID09ICJWaWV0TmFtIiwgIkNvdW50cnkiXSA8LSAiVmlldCBOYW0iCgpgYGAKVGhhdCB0b29rIGEgV0hJTEUhIEJ1dCwgbm93IG15IHR3byBkYXRhc2V0cyBzaG91bGQgYmUgYmV0dGVyIG1hdGNoZWQsIGFuZCBpdCdsbCBiZSBtdWNoIGVhc2llciB0byBqb2luIHRoZW0hIAoKTm93IEknbSBnb2luZyB0byByZW1vdmUgc29tZSB1bm5lY2Vzc2FyeSB2YXJpYWJsZXMgZnJvbSB0aGUgZGF0YXNldHMgVGhpcyBpcyBiZWNhdXNlIHNvbWUgdmFyaWFibGVzIGhhdmUgdG9vIG1hbnkgbWlzc2luZyBkYXRhcG9pbnRzIHRvIGJlIHVzZWZ1bCwgb3IgYmVjYXVzZSBzb21lIHZhcmlhYmxlcyBtYXkgYmUgcmVkdW5kYW50LiAKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQoKbGVkXzIwMTUgPSBzdWJzZXQobGVkXzIwMTUsIHNlbGVjdCA9IC1jKEFsY29ob2wsIFRvdGFsZXhwZW5kaXR1cmUsWWVhcikpCgpgYGAKCk5vdywgSSB3aWxsIGJlIGpvaW5pbmcgdGhlIHR3byBkYXRhc2V0cyB1c2luZyBpbm5lcl9qb2luLCBiZWNhdXNlIEkgb25seSB3YW50IHRoZSBjb3VudHJpZXMgaW4gY29tbW9uIGJldHdlZW4gYm90aCBkYXRhZnJhbWVzLiAgSSdtIGFsc28gZ29pbmcgdG8gY3JlYXRlIGEgc2Vjb25kIHZlcnNpb24gdGhhdCBpcyBmdWxsX2pvaW5lZCwganVzdCBpbiBjYXNlIEkgZW5kIHVwIG5lZWRpbmcgaXQhCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGF0YXNldCA8LSB3ZWklPiUgaW5uZXJfam9pbihsZWRfMjAxNSxieT0iQ291bnRyeSIpCgpkYXRhc2V0MiA8LSB3ZWklPiUgZnVsbF9qb2luKGxlZF8yMDE1LGJ5PSJDb3VudHJ5IikKZGltKGRhdGFzZXQpCmBgYAoKRmluYWxseSwgSSB3aWxsIGJlIHJlbmFtaW5nIGEgZmV3IHZhcmlhYmxlcyBiZWNhdXNlIEkgZGlzbGlrZSBoYXZpbmcgc3BhY2VzIGluIG15IHZhcmlhYmxlIG5hbWVzLiBBZnRlciB0aGlzLCBJIHdpbGwgaGF2ZSBmaW5pc2hlZCBjbGVhbmluZyB0aGUgZGF0YSEgSSdtIGFsc28gcmVtb3ZpbmcgYSBmZXcgbW9yZSB2YXJpYWJsZXMganVzdCBiZWNhdXNlIHRoZXkgc2VlbSByYXRoZXIgcmVkdW5kYW50IC0gZm9yIGV4YW1wbGUsIHRoZSBHZW5kZXIgUGFyaXR5IEdyb3VwIGFuZCBHZW5kZXIgUGFyaXR5IEluZGV4IHZhcmlhYmxlcyBtYXkgYmUgdG9vIHNpbWlsYXIgdG8gV0VJLCB0aGVyZWZvcmUgdGhleSBtaWdodCBoYXZlIHNrZXdlZCBsZXZlbHMgb2YgY29ycmVsYXRpb24uCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShXRUkgPSAiV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCAoV0VJKSAtIDIwMjIiKQoKZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShXRUcgPSAiV29tZW4ncyBFbXBvd2VybWVudCBHcm91cCAtIDIwMjIiKQoKZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShHR1BJID0gIkdsb2JhbCBHZW5kZXIgUGFyaXR5IEluZGV4IChHR1BJKSAtIDIwMjIiKQoKZGF0YXNldCA8LSBkYXRhc2V0ICU+JQogIHJlbmFtZShIREcgPSAiSHVtYW4gRGV2ZWxvcG1lbnQgR3JvdXAgLSAyMDIxIikKCmRhdGFzZXQgPC0gZGF0YXNldCAlPiUKICByZW5hbWUoR1BHID0gIkdlbmRlciBQYXJpdHkgR3JvdXAgLSAyMDIyIikKCmRhdGFzZXQgPC0gZGF0YXNldCAlPiUKICByZW5hbWUoU0RHX1JlZ2lvbiA9ICJTdXN0YWluYWJsZSBEZXZlbG9wbWVudCBHb2FsIHJlZ2lvbnMiKQpgYGAKCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGF0YXNldCA9IHN1YnNldChkYXRhc2V0LCBzZWxlY3QgPSAtYyhHUEcsIFNER19SZWdpb24sR0dQSSkpCmBgYApBbGwgZmluaXNoZWQgY2xlYW5pbmchIE5vdyBJIHdhbnQgdG8gdGFrZSBhIHBlZWsgYXQgdGhlIGRhdGFzZXQgSSdsbCBiZSB3b3JraW5nIHdpdGggYmVmb3JlIEkgZ28gaW50byB0aGUgbmV4dCBzdGVwcy4gCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0KZGltKGRhdGFzZXQpCnN1bW1hcnkoZGF0YXNldCRXRUkpCnRhbGx5KGRhdGFzZXQkU3RhdHVzKQpgYGAKIyBJbnRpYWwgVmlzdWFsaXphdGlvbiAKTm93LCBJIHdhbnQgdG8gY3JlYXRlIGEgbWFwIG9mIFdFSSBieSBjb3VudHJ5LCBqdXN0IHRvIGdldCBhIGdlbmVyYWwgdmlzdWFsIG9mIGhvdyB0aGUgZGlzdHJpYnV0aW9uIGxvb2tzLiAKCkknbSBnb2luZyB0byBzdGFydCBvdXQgYnkgY3JlYXRpbmcgYSBqb2luZWQgZGF0YXNldCBiZXR3ZWVuICpkYXRhc2V0KiBhbmQgKldvcmxkKiwgc28gSSBjYW4gaGF2ZSBhIG1hcHBhYmxlIGRhdGFzZXQgdXNpbmcgdGhlICoqdG1hcCoqIHBhY2thZ2UuIApgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9CmxpYnJhcnkodG1hcCkKZGF0YSgiV29ybGQiKQoKZGF0YXNldDwtIGRhdGFzZXQgJT4lCiAgcmVuYW1lKG5hbWUgPSBDb3VudHJ5KQoKd2VpLm1hcCA8LSBpbm5lcl9qb2luKGRhdGFzZXQsV29ybGQsYnk9Im5hbWUiKQoKYGBgClVuZm9ydHVuYXRlbHksIHRoZSAqd2VpLm1hcCogb25seSBjb250YWlucyA5OCBpbmRpdmlkdWFscyBkZXNwaXRlICpkYXRhc2V0KiBjb250YWluaW5nIDExMiBpbmRpdmlkdWFscyEgVGhpcyBtZWFucyB0aGF0IDE0IGluZGl2aWR1YWxzIGhhdmUgZGlzaGFybW9uaW91cyBuYW1lcyBiZXR3ZWVuICpkYXRhc2V0KiBhbmQgKldvcmxkKi4gSSdtIGdvaW5nIHRvIHJ1biBzb21lIGNvZGUgcmVuYW1pbmcgYSB3aG9sZSBidW5jaCBvZiBjb3VudHJpZXMsIGp1c3QgbGlrZSBJIGRpZCBhYm92ZSB3aXRoIHRoZSAqbGVkXzIwMTUqIGRhdGFzZXQuIEknbSBnb2luZyB0byBoaWRlIHRoaXMgY29kZSwganVzdCBiZWNhdXNlIGl0J3Mgc3VwZXIgdGVkaW91cyB3b3JrLiBUaWR5aW5nICYgY2xlYW5pbmcgeW91ciBkYXRhIGlzIGEgY29uc3RhbnQgcHJvY2VzcyEKYGBge3J9CmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJCb3NuaWEgYW5kIEhlcnplZ292aW5hIiwgIm5hbWUiXSA8LSAiQm9zbmlhIGFuZCBIZXJ6LiIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJEZW1vY3JhdGljUmVwdWJsaWNvZnRoZUNvbmdvIiwgIm5hbWUiXSA8LSAiRGVtLiBSZXAuIENvbmdvIgoKZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIkRvbWluaWNhbiBSZXB1YmxpYyIsICJuYW1lIl0gPC0gIkRvbWluaWNhbiBSZXAuIgoKZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIklyYW4gKElzbGFtaWMgUmVwdWJsaWMgb2YpIiwgIm5hbWUiXSA8LSAiSXJhbiIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJMYW9zIiwgIm5hbWUiXSA8LSAiTGFvIFBEUiIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJOb3J0aCBNYWNlZG9uaWEiLCAibmFtZSJdIDwtICJNYWNlZG9uaWEiCgpkYXRhc2V0W2RhdGFzZXQkbmFtZSA9PSAiTW9sZG92YSAoUmVwdWJsaWMgb2YpIiwgIm5hbWUiXSA8LSAiTW9sZG92YSIKCmRhdGFzZXRbZGF0YXNldCRuYW1lID09ICJUYW56YW5pYSAoVW5pdGVkIFJlcHVibGljIG9mKSIsICJuYW1lIl0gPC0gIlRhbnphbmlhIgoKZGF0YXNldFtkYXRhc2V0JG5hbWUgPT0gIlZpZXQgTmFtIiwgIm5hbWUiXSA8LSAiVmlldG5hbSIKYGBgCk5vdywgbGV0J3MgdHJ5IHJlLWpvaW5pbmcgdGhlc2UgZGF0YXNldHMhCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IixtZXNzYWdlPUZBTFNFfQp3ZWkubWFwIDwtIHJpZ2h0X2pvaW4oZGF0YXNldCxXb3JsZCxieT0ibmFtZSIpCmxpYnJhcnkoczIpCmxpYnJhcnkoc2YpCndlaS5tYXAuc2YgIDwtIHdlaS5tYXAgJT4lIAogIHN0X2FzX3NmKCkKYGBgCkFuZCB2aXN1YWxpemluZzoKYGBge3IsIG1lc3NhZ2U9RkFMU0V9Cgp0bWFwX21vZGUoInBsb3QiKQp0bV9zaGFwZSh3ZWkubWFwLnNmKSArCiAgICB0bV9wb2x5Z29ucygiV0VJIixwYWxldHRlPSJtYWdtYSIsbj04KSArCnRtX2xheW91dChpbm5lci5tYXJnaW5zID0gYyguMDUsIC4wNSwgLjA1LCAuMDUpLCBzZXBpYS5pbnRlbnNpdHk9LjAxLCBmcmFtZS5kb3VibGUubGluZT0xLCB0aXRsZSA9ICJQbG90IG9mIFdvbWVuJ3MgRW1wb3dlcm1lbnQgSW5kZXggU2NvcmVzIiwgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgdGl0bGUuZm9udGZhY2U9ImJvbGQiLHRpdGxlLmZvbnRmYW1pbHk9IlRpbWVzIiwgbGVnZW5kLnRpdGxlLmZvbnRmYW1pbHkgPSAiVGltZXMiLGxlZ2VuZC50aXRsZS5mb250ZmFjZT0iYm9sZCIsIHRpdGxlLmJnLmNvbG9yPSJsaWdodHNhbG1vbiIsIHRpdGxlLnNpemU9MS44LCBsZWdlbmQucG9zaXRpb249YygibGVmdCIsImNlbnRlciIpLCBzYXR1cmF0aW9uID0gLjksIHRpdGxlLmJnLmFscGhhPS43KQoKYGBgCkxvdmVseSEgSSBhbHNvIG1hZGUgYW4gaW50ZXJhY3RpdmUgdmVyc2lvbiBvZiB0aGUgbWFwLCBqdXN0IGZvciBmdXJ0aGVyIGV4cGxvcmF0aW9uIG9mIHRoZSB0bWFwIHBhY2thZ2UuCgpgYGB7ciwgbWVzc2FnZT1GQUxTRX0KCnRtYXBfbW9kZSgidmlldyIpCnRtX3NoYXBlKHdlaS5tYXAuc2YpICsKICAgIHRtX3BvbHlnb25zKCJXRUkiLHBhbGV0dGU9Im1hZ21hIixuPTgpICsKdG1fbGF5b3V0KGlubmVyLm1hcmdpbnMgPSBjKC4wNSwgLjA1LCAuMDUsIC4wNSksIHNlcGlhLmludGVuc2l0eT0uMDEsIGZyYW1lLmRvdWJsZS5saW5lPTEsIHRpdGxlID0gIlBsb3Qgb2YgV29tZW4ncyBFbXBvd2VybWVudCBJbmRleCBTY29yZXMiLCB0aXRsZS5wb3NpdGlvbiA9IGMoInJpZ2h0IiwgImJvdHRvbSIpLCB0aXRsZS5mb250ZmFjZT0iYm9sZCIsdGl0bGUuZm9udGZhbWlseT0iVGltZXMiLCBsZWdlbmQudGl0bGUuZm9udGZhbWlseSA9ICJUaW1lcyIsbGVnZW5kLnRpdGxlLmZvbnRmYWNlPSJib2xkIiwgdGl0bGUuYmcuY29sb3I9ImxpZ2h0c2FsbW9uIiwgdGl0bGUuc2l6ZT0xLjgsIGxlZ2VuZC5wb3NpdGlvbj1jKCJsZWZ0IiwiY2VudGVyIiksIHNhdHVyYXRpb24gPSAuOSwgdGl0bGUuYmcuYWxwaGE9LjcpCgpgYGAKTm93LCBJIHdhbnQgdG8gbW92ZSBvbnRvIGNyZWF0aW5nIG1vZGVscyB0aGF0IHdpbGwgaGVscCBtZSBwcmVkaWN0IHRoZSBXRUkgc2NvcmVzIG9mIGNvdW50cmllcyBiYXNlZCBvbiBvdGhlciBmYWN0b3JzLgoKIyBEZWNpc2lvbiBUcmVlCkknbSBnb2luZyB0byBzdGFydCBieSBjcmVhdGluZyBhIGRlY2lzaW9uIHRyZWUgZm9yIG15ICpkYXRhc2V0Ki4gSSBiZWxpZXZlIHRoaXMgd2lsbCBiZSBiZW5lZmljaWFsIHRvIHNlZSwgc2VlaW5nIGFzIHRoZSBkZWNpc2lvbiB0cmVlIHdpbGwgc2VsZWN0IHRoZSBiZXN0IHByZWRpY3RvcnMgZm9yIG15IHJlc3BvbnNlIHZhcmlhYmxlLiBUaGUgZmlyc3Qgc3RlcCBoZXJlIGlzIHRvIHNwbGl0IG15IGRhdGEgaW50byB0cmFpbmluZyBhbmQgdGVzdGluZyBkYXRhLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsbWVzc2FnZT1GQUxTRX0KCnNldC5zZWVkKDIyOCkKdHJhaW4uaW5kZXggPC0gY3JlYXRlRGF0YVBhcnRpdGlvbihkYXRhc2V0JFdFSSwgcD0gMC44LCBsaXN0ID0gRkFMU0UpCnRyYWluIDwtIGRhdGFzZXRbdHJhaW4uaW5kZXgsXQp0ZXN0IDwtIGRhdGFzZXRbLXRyYWluLmluZGV4LF0KYGBgCkknbSBnb2luZyB0byBsb29rIGF0IHRoZSBwcm9wb3J0aW9uIG9mIGRldmVsb3BlZCB2cyBkZXZlbG9waW5nIGNvdW50cmllcyB3aXRoaW4gYm90aCBteSAqdGVzdCogYW5kICp0cmFpbiogZGF0YS4gVGhpcyBpcyBzbyBJIGNhbiBlbnN1cmUgdGhhdCAqdGVzdCogaXMgYXQgbGVhc3Qgc29tZXdoYXQgcmVwcmVzZW50YXRpdmUgb2YgdGhlIGRpc3RyaWJ1dGlvbiB3aXRoaW4gKnRyYWluKi4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLG1lc3NhZ2U9RkFMU0V9CnRhbGx5KHRlc3QkU3RhdHVzLCBmb3JtYXQ9J3Byb3BvcnRpb24nKQp0YWxseSh0cmFpbiRTdGF0dXMsIGZvcm1hdCA9ICdwcm9wb3J0aW9uJykKdGFsbHkoZGF0YXNldCRTdGF0dXMsIGZvcm1hdCA9ICdwcm9wb3J0aW9uJykKYGBgCkJvdGggKnRlc3QqIGFuZCAqdHJhaW4qIGFwcGVhciB0byBiZSBhZGVxdWF0ZWx5IHJlcHJlc2VudGF0aXZlIG9mICpkYXRhc2V0KiEKCk5vdywgSSB3aWxsIGNyZWF0ZSBteSBkZWNpc2lvbiB0cmVlLiBJIHdpbGwgZml0IHRoZSBkZWNpc2lvbiB0cmVlIHRvICp0cmFpbiosIGFuZCBJIHdpbGwgc2V0IG5lYXJseSBldmVyeSBwcmVkaWN0b3IgdmFyaWFibGUgKEJFU0lERVMgTkFNRSAmIFdFRywgZm9yIHJlYXNvbnMgb2YgcmVkdW5kYW5jeSkgYXMgbXkgWC4gSSBhbSBkb2luZyB0aGlzIHNvIHRoYXQgdGhlIGRlY2lzaW9uIHRyZWUgY2FuIHRlbGwgbWUgd2hpY2ggcHJlZGljdG9yIHZhcmlhYmxlcyBoYXZlIHRoZSBoaWdoZXN0IGluZmx1ZW5jZSBvbiBteSByZXNwb25zZSB2YXJpYWJsZSwgV0VJLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsbWVzc2FnZT1GQUxTRX0KdHJlZSA8LSBycGFydChXRUl+IEhERyArIFN0YXR1cyArIExpZmVleHBlY3RhbmN5ICsgQWR1bHRNb3J0YWxpdHkgKyBpbmZhbnRkZWF0aHMgKyBIZXBhdGl0aXNCICsgTWVhc2xlcyArICBQb2xpbyArIERpcGh0aGVyaWEgKyBQb3B1bGF0aW9uICsgU2Nob29saW5nLCBkYXRhPXRyYWluLCBjcD0uMDEpCgpycGFydC5wbG90KHRyZWUpCmBgYAoKSXQgYXBwZWFycyB0aGVyZSBhcmUgdGhyZWUgcHJlZGljdG9ycyB0aGF0IGhhdmUgdGhlIGhpZ2hlc3QgY29ycmVsYXRpb24gd2l0aCBXRUkgc2NvcmVzIDogSERHIChIdW1hbiBEZXZlbG9wbWVudCBHcm91cCksIFllYXJzIG9mIFNjaG9vbGluZywgYW5kIEFkdWx0IE1vcnRhbGl0eSBSYXRlcy4gRm9yIGZ1cnRoZXIgZXhwbG9yYXRpb24sIEknbSBnb2luZyB0byBjcmVhdGUgYSBSYW5kb20gRm9yZXN0IHRvIGZpbmQgb3VyIGJlc3QgbW9kZWwuCgojIFJhbmRvbSBGb3Jlc3QgJiBSTVNFCkknbSBnb2luZyB0byBoYXZlIHRvIG5vdCB1c2UgYW55IHZhcmlhYmxlcyB0aGF0IGhhdmUgIk5BIiB2YWx1ZXMgdG8gYmUgYWJsZSB0byBwcm9wZXJseSBjcmVhcnRlIGEgcmFuZG9tIGZvcmVzdC4gVGhpcyB1bmZvcnR1bmF0ZWx5IG1lYW5zIHJlbW92aW5nIFNjaG9vbGluZy4gSG93ZXZlciwgSSBkb24ndCB3YW50IHRvIHJlbW92ZSBTY2hvb2xpbmcgYmVjYXVzZSBhY2NvcmRpbmcgdG8gdGhlIGRlY2lzaW9uIHRyZWUsIGl0J3Mgb25lIG9mIHRoZSBtb3N0IGltcG9ydGFudCBwcmVkaWN0b3JzISBTbywgSSBhbSBnb2luZyB0byB1c2UgcmVncmVzc2lvbiBpbXB1dGF0aW9uIHRvIGNyZWF0ZSBzb21lIGVzdGltYXRlZCBTY2hvb2xpbmcgdmFsdWVzIGZvciBhbnkgb2Ygb3VyIGNvdW50cmllcyB0aGF0IGhhdmUgU2Nob29saW5nID0gTkEuIApgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIG1lc3NhZ2U9RkFMU0V9Cm1pc3NpbmcgPC0gaXMubmEoZGF0YXNldCRTY2hvb2xpbmcpIAoKc3VtKG1pc3NpbmcpCgp3aGljaChtaXNzaW5nKQoKbG0oU2Nob29saW5nfldFSSwgZGF0YT1kYXRhc2V0KQoKCmRhdGFzZXQkU2Nob29saW5nWzExXTwtMi41NzQrMTcuOTQzKigwLjcwNykKZGF0YXNldCRTY2hvb2xpbmdbMzBdPC0yLjU3NCsxNy45NDMqKDAuNzc4KQpkYXRhc2V0JFNjaG9vbGluZ1szMV08LTIuNTc0KzE3Ljk0MyooMC43NTIpCmRhdGFzZXQkU2Nob29saW5nWzQ3XTwtMi41NzQrMTcuOTQzKigwLjY4NikKZGF0YXNldCRTY2hvb2xpbmdbNzJdPC0yLjU3NCsxNy45NDMqKDAuMzk5KQpkYXRhc2V0JFNjaG9vbGluZ1sxMDVdPC0yLjU3NCsxNy45NDMqKDAuNTEwKQpgYGAKWWV0IGFub3RoZXIgZXhhbXBsZSBvZiBuZXZlciBiZWluZyBkb25lIHdpdGggY2xlYW5pbmcgdGhlIGRhdGEhIE5vdywgSSBoYXZlIHRvIHJlY3JlYXRlIG15IHRyYWluaW5nIGFuZCB0ZXN0aW5nIGRhdGFzZXQgd2l0aG91dCB0aGVzZSBtaXNzaW5nIHZhbHVlcy4gCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgbWVzc2FnZT1GQUxTRX0Kc2V0LnNlZWQoMjI4KQp0cmFpbi5pbmRleCA8LSBjcmVhdGVEYXRhUGFydGl0aW9uKGRhdGFzZXQkV0VJLCBwPSAwLjgsIGxpc3QgPSBGQUxTRSkKdHJhaW4gPC0gZGF0YXNldFt0cmFpbi5pbmRleCxdCnRlc3QgPC0gZGF0YXNldFstdHJhaW4uaW5kZXgsXQpgYGAKClRoZW4sIEkgY2FuIGZpbmFsbHkgY3JlYXRlIG15IFJhbmRvbSBGb3Jlc3QKYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDIyOCkKcmZfbW9kZWwgPC1yYW5kb21Gb3Jlc3QoV0VJIH4gSERHICsgU3RhdHVzICsgTGlmZWV4cGVjdGFuY3kgKyBTY2hvb2xpbmcgKyBBZHVsdE1vcnRhbGl0eSArIGluZmFudGRlYXRocyArIE1lYXNsZXMgKyAgUG9saW8gKyBEaXBodGhlcmlhICxkYXRhPXRyYWluLCBwcm94aW1pdHk9VFJVRSwgbnRyZWU9MTAwMCkKcmZfbW9kZWwKCmBgYApUaGUgbWVhbiBvZiBzcXVhcmVkIHJlc2lkdWFscyBpcyBwcmV0dHkgY2xvc2UgdG8gMCwgd2hpY2ggbWVhbnMgdGhlIG1vZGVsIGlzIGdvb2QhIEhvd2V2ZXIsIEknbSBnb2luZyB0byBhbHNvIHRyeSBydW5uaW5nIGEgUmFuZG9tIEZvcmVzdCBtb2RlbCBpbiB3aGljaCBJIG9ubHkgdXNlIHRoZSB0aHJlZSBiZXN0IHByZWRpY3RvciB2YXJpYWJsZXMgYXMgaWRlbnRpZmllZCBieSBteSBEZWNpc2lvbiBUcmVlLiBJIHdpbGwgdGhlbiBjb21wYXJlIHRoZSB0d28gbWVhbiBvZiBzcXVhcmVkIHJlc2lkdWFscy4KYGBge3IsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLG1lc3NhZ2U9RkFMU0V9CnNldC5zZWVkKDIyOCkKcmZfbW9kZWwyIDwtcmFuZG9tRm9yZXN0KFdFSSB+IEhERyArICBTY2hvb2xpbmcgKyBBZHVsdE1vcnRhbGl0eSAgLGRhdGE9dHJhaW4sIHByb3hpbWl0eT1UUlVFLCBudHJlZT0xMDAwKQpyZl9tb2RlbDIKCmBgYApUaGUgbWVhbiBvZiBzcXVhcmVkIHJlc2lkdWFscyBpcyBldmVyIHNvIHNsaWdodGx5IGhpZ2hlciBmb3IgdGhpcyBtb2RlbCwgYW5kIHRoZSBWYXIgZXhwbGFpbmVkIGlzIHNsaWdodGx5IGxvd2VyLiBJJ20gZ29pbmcgdG8gdXNlIGFuIFJNU0UgdGVzdCB0byBjb21wYXJlIHRoZSB0d28gbW9kZWxzLgoKCk5vdywgSSdtIGdvaW5nIHRvIGJvb3N0IHRoZSB0d28gbW9kZWxzLiBCZWZvcmUgZG9pbmcgc28sIEkgYW0gY2hhbmdpbmcgSERHJ3MgY2xhc3MgdG8gb3JkZXJlZCwgYW5kIFN0YXR1cydzIGNsYXNzIHRvIG9yZGVyZWQsIHNvIHRoZXkgYXJlY29tcGF0aWJsZSB3aXRoIHRoZSBib29zdGVkIG1vZGVsLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsbWVzc2FnZT1GQUxTRX0KdHJhaW4kSERHIDwtIGFzLm9yZGVyZWQodHJhaW4kSERHKQp0cmFpbiRTdGF0dXMgPC0gYXMub3JkZXJlZCh0cmFpbiRTdGF0dXMpCmJvb3N0MSA8LSBnYm0oV0VJIH4gSERHICsgU3RhdHVzICsgTGlmZWV4cGVjdGFuY3kgKyBTY2hvb2xpbmcgKyBBZHVsdE1vcnRhbGl0eSArIGluZmFudGRlYXRocyArIE1lYXNsZXMgKyAgUG9saW8gKyBEaXBodGhlcmlhLCBkYXRhPXRyYWluLGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsIG4udHJlZXMgPSAxMDAwLCBzaHJpbmthZ2UgPSAwLjAxLCBuLm1pbm9ic2lubm9kZT01KQpib29zdDIgPC0gZ2JtKFdFSX4gU2Nob29saW5nICsgSERHICsgQWR1bHRNb3J0YWxpdHkgLCBkYXRhID0gdHJhaW4sIGRpc3RyaWJ1dGlvbiA9ICJnYXVzc2lhbiIsIG4udHJlZXMgPSAxMDAwLCBzaHJpbmthZ2UgPSAwLjAxLCBuLm1pbm9ic2lubm9kZT01KQoKYGBgCk5vdyBJIHdpbGwgZXZhbHVhdGUgdGhlIHR3byBtb2RlbHMgdXNpbmcgcHJlZGljdGlvbnMgJiBSTVNFLgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KcHJlZGljdGlvbnMxIDwtIHByZWRpY3QoYm9vc3QxLG5ld2RhdGE9dGVzdCkKcm1zZShhY3R1YWw9dGVzdCRXRUksIHByZWRpY3RlZD1wcmVkaWN0aW9uczEpCgpwcmVkaWN0aW9uczIgPC0gcHJlZGljdChib29zdDIsIG5ld2RhdGEgPSB0ZXN0KQoJcm1zZShhY3R1YWw9dGVzdCRXRUksIHByZWRpY3RlZD1wcmVkaWN0aW9uczIpCmBgYApPbmNlIGFnYWluLCB0aGUgbW9kZWwgd2l0aCBuZWFybHkgdmVyeSB2YXJpYWJsZSBoYXMgcHJvdmVkIHRvIHBlcmZvcm0gKnNsaWdodGx5KiBiZXR0ZXIgdGhhbiB0aGUgbW9kZWwgd2l0aCBvbmx5IDMgcHJlZGljdG9yIHZhcmlhYmxlcy4gSG93ZXZlciwgdGhpcyBkaWZmZXJlbmNlIGlzICoqc28gc21hbGwqKiwgYW5kIEkgdGhpbmsgaXQgaXMgd29ydGggaXQgdG8gc2FjcmlmaWNlIHRoaXMgc21hbGwgZGlmZmVyZW5jZSBmb3IgdGhlIHNha2Ugb2Yga2VlcGluZyBvdXIgbW9kZWwgU0lNUExFISBUaGUgbW9kZWwgc3RpbGwgcGVyZm9ybXMgdmVyeSB3ZWxsLiBTbywgSSdtIGRlY2lkaW5nIHRvIGtlZXAgdGhlIG1vZGVsIHRoYXQgb25seSBjb250YWlucyB0aGUgdGhyZWUgYmVzdCBwcmVkaWN0b3IgdmFyaWFibGVzISAKCiMgRmluYWwgVmlzdWFsaXphdGlvbnMKYGBge3IsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0Kd2VpLm1hcCA8LSByaWdodF9qb2luKGRhdGFzZXQsV29ybGQsYnk9Im5hbWUiKQoKd2VpLm1hcC5zZiAgPC0gd2VpLm1hcCAlPiUgCiAgc3RfYXNfc2YoKQpgYGAKSSdtIGdvaW5nIHRvIGNyZWF0ZSBGT1VSIG1hcHMgaGVyZTogT25lIGV4YWN0bHkgbGlrZSBteSBmaXJzdCB2aXN1YWxpemF0aW9uIHRoYXQgc2hvd3MgV0VJIHNjb3JlcyBieSBjb3VudHJ5LCBhbmQgdGhlbiB0aHJlZSB0aGF0IHNob3dzIHRoZSBkaXN0cmlidXRpb24gb2YgZWFjaCBvZiBteSBwcmVkaWN0b3IgdmFyaWFibGVzISBJIHdpbGwgZGlzcGxheSBhbGwgb2YgdGhlc2UgbWFwcyBzaWRlIGJ5IHNpZGUuCmBgYHtyLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQp0bWFwX21vZGUoInBsb3QiKQoKIHRtX3NoYXBlKHdlaS5tYXAuc2YpICsgdG1fcG9seWdvbnMoIldFSSIscGFsZXR0ZT0ibWFnbWEiLG49OCkgKwp0bV9sYXlvdXQoaW5uZXIubWFyZ2lucyA9IGMoLjA1LCAuMDUsIC4wNSwgLjA1KSwgc2VwaWEuaW50ZW5zaXR5PS4wMSwgZnJhbWUuZG91YmxlLmxpbmU9MSwgdGl0bGUgPSAiUGxvdCBvZiBXb21lbidzIEVtcG93ZXJtZW50IEluZGV4IFNjb3JlcyIsIHRpdGxlLnBvc2l0aW9uID0gYygicmlnaHQiLCAiYm90dG9tIiksIHRpdGxlLmZvbnRmYWNlPSJib2xkIix0aXRsZS5mb250ZmFtaWx5PSJUaW1lcyIsIGxlZ2VuZC50aXRsZS5mb250ZmFtaWx5ID0gIlRpbWVzIixsZWdlbmQudGl0bGUuZm9udGZhY2U9ImJvbGQiLCB0aXRsZS5iZy5jb2xvcj0ibGlnaHRzYWxtb24iLCB0aXRsZS5zaXplPTEuOCwgbGVnZW5kLnBvc2l0aW9uPWMoImxlZnQiLCJjZW50ZXIiKSwgc2F0dXJhdGlvbiA9IC45LCB0aXRsZS5iZy5hbHBoYT0uNykKCnRtX3NoYXBlKHdlaS5tYXAuc2YpICsgdG1fcG9seWdvbnMoIkhERyIscGFsZXR0ZT0ibWFnbWEiLG49OCkgKwp0bV9sYXlvdXQoaW5uZXIubWFyZ2lucyA9IGMoLjA1LCAuMDUsIC4wNSwgLjA1KSwgc2VwaWEuaW50ZW5zaXR5PS4wMSwgZnJhbWUuZG91YmxlLmxpbmU9MSwgdGl0bGUgPSAiUGxvdCBvZiBIdW1hbiBEZXZlbG9wbWVudCBHcm91cHMiLCB0aXRsZS5wb3NpdGlvbiA9IGMoInJpZ2h0IiwgImJvdHRvbSIpLCB0aXRsZS5mb250ZmFjZT0iYm9sZCIsdGl0bGUuZm9udGZhbWlseT0iVGltZXMiLCBsZWdlbmQudGl0bGUuZm9udGZhbWlseSA9ICJUaW1lcyIsbGVnZW5kLnRpdGxlLmZvbnRmYWNlPSJib2xkIiwgdGl0bGUuYmcuY29sb3I9ImxpZ2h0c2FsbW9uIiwgdGl0bGUuc2l6ZT0xLjgsIGxlZ2VuZC5wb3NpdGlvbj1jKCJsZWZ0IiwiY2VudGVyIiksIHNhdHVyYXRpb24gPSAuOSwgdGl0bGUuYmcuYWxwaGE9LjcpCgp0bV9zaGFwZSh3ZWkubWFwLnNmKSArIHRtX3BvbHlnb25zKCJTY2hvb2xpbmciLHBhbGV0dGU9Im1hZ21hIixuPTgpICsKdG1fbGF5b3V0KGlubmVyLm1hcmdpbnMgPSBjKC4wNSwgLjA1LCAuMDUsIC4wNSksIHNlcGlhLmludGVuc2l0eT0uMDEsIGZyYW1lLmRvdWJsZS5saW5lPTEsIHRpdGxlID0gIlBsb3Qgb2YgWWVhcnMgb2YgU2Nob29saW5nIiwgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgdGl0bGUuZm9udGZhY2U9ImJvbGQiLHRpdGxlLmZvbnRmYW1pbHk9IlRpbWVzIiwgbGVnZW5kLnRpdGxlLmZvbnRmYW1pbHkgPSAiVGltZXMiLGxlZ2VuZC50aXRsZS5mb250ZmFjZT0iYm9sZCIsIHRpdGxlLmJnLmNvbG9yPSJsaWdodHNhbG1vbiIsIHRpdGxlLnNpemU9MS44LCBsZWdlbmQucG9zaXRpb249YygibGVmdCIsImNlbnRlciIpLCBzYXR1cmF0aW9uID0gLjksIHRpdGxlLmJnLmFscGhhPS43KQoKdG1fc2hhcGUod2VpLm1hcC5zZikgKyB0bV9wb2x5Z29ucygiQWR1bHRNb3J0YWxpdHkiLHBhbGV0dGU9Im1hZ21hIixuPTgpICsKdG1fbGF5b3V0KGlubmVyLm1hcmdpbnMgPSBjKC4wNSwgLjA1LCAuMDUsIC4wNSksIHNlcGlhLmludGVuc2l0eT0uMDEsIGZyYW1lLmRvdWJsZS5saW5lPTEsIHRpdGxlID0gIkFkdWx0IE1vcnRhbGl0eSBwZXIgUG9wdWxhdGlvbiBvZiAxMDAwIiwgdGl0bGUucG9zaXRpb24gPSBjKCJyaWdodCIsICJib3R0b20iKSwgdGl0bGUuZm9udGZhY2U9ImJvbGQiLHRpdGxlLmZvbnRmYW1pbHk9IlRpbWVzIiwgbGVnZW5kLnRpdGxlLmZvbnRmYW1pbHkgPSAiVGltZXMiLGxlZ2VuZC50aXRsZS5mb250ZmFjZT0iYm9sZCIsIHRpdGxlLmJnLmNvbG9yPSJsaWdodHNhbG1vbiIsIHRpdGxlLnNpemU9MS44LCBsZWdlbmQucG9zaXRpb249YygibGVmdCIsImNlbnRlciIpLCBzYXR1cmF0aW9uID0gLjksIHRpdGxlLmJnLmFscGhhPS43KQpgYGAKClRoZXNlIGFyZSBzb3J0IG9mIGNvbmZ1c2luZy4gRm9yIG9uZSwgdGhlIEhERyBjYXRlZ29yaWVzIGFyZSBvdXQgb2Ygb3JkZXIsIHdoaWNoIGlzIHZlcnkgaGFyZCB0byByZWFkLiBTZWNvbmRseSwgdGhlIGFkdWx0IG1vcnRhbGl0eSBtYXAgc2hvdWxkIHByb2JhYmx5IGhhdmUgdGhlIGNvbG9ycyBmbGlwcGVkLCBiZWNhdXNlIGxvd2VyIGFkdWx0IG1vcnRhbGl0eSBjb3JyZXNwb25kcyB0byAqaGlnaGVyKiBXRUkuCgpJJ20gZ29pbmcgdG8gZml4IHRoZW0sIGFuZCBpbnNlcnQgdGhlIHBuZyBiZWxvdy4KCmBgYHtyfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiL1VzZXJzL2phc21pbmVkYXkvRG93bmxvYWRzL1dFSVBMT1QuanBnIikKYGBgCgogQW5kIHRoZXJlIHdlIGhhdmUgb3VyIGZpbmFsIHZpc3VhbGl6YXRpb24hICBJIGhvcGUgeW91IGVuam95ZWQgcmVhZGluZyB0aHJvdWdoIHRoaXMgcHJvamVjdCBhcyBtdWNoIGFzIEkgZW5qb3llZCBteSBTVEFUMjI4IGNsYXNzIDopCiAKIyBCT05VUzogVGVzdGluZyB0aGUgUHJlZGljdGlvbiBNb2RlbApBZnRlciBmaW5pc2hpbmcgdGhlIGZpcnN0IGRyYWZ0IG9mIG15IHByb2plY3QsIEkgZmlndXJlZCBJIHdhbnRlZCB0byBhZGQgYSBzZWN0aW9uIGluIHdoaWNoIEkgdGFrZSBhIHNtYWxsIGhhbmRmdWwgb2YgY291bnRyaWVzIHRoYXQgYXJlIE5PVCByZXByZXNlbnRlZCBpbiB0aGUgKndlaSogZGF0YXNldCBhbmQgdXNlIG91ciBtb2RlbCB0byBwcmVkaWN0IHRoZSBXRUkgc2NvcmVzIGZvciB0aGVzZSBuYXRpb25zLiBJIHdpbGwgYmUgdXNpbmcgdGhlIGNvdW50cmllcyBLYXpha2hzdGFuLCBOZXcgWmVhbGFuZCwgU3VkYW4sIEhhaXRpLCBhbmQgVWtyYWluZS4gSSB3aWxsIHVzZSAqbGVkXzIwMTUqIHRvIHNvdXJjZSB0aGUgSERHLCBTY2hvb2xpbmcsIGFuZCBBZHVsdE1vcnRhbGl0eSBmb3IgZWFjaCBvZiB0aGVzZSBjb3VudHJpZXMuCgpgYGB7ciwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0KZmluYWxtb2RlbCA8LSBsbShXRUl+SERHK1NjaG9vbGluZytBZHVsdE1vcnRhbGl0eSxkYXRhPWRhdGFzZXQpCmZpbmFsbW9kZWwKI0themFraHN0YW46CiAgI0hERz1WZXJ5IGhpZ2gKICAjU2Nob29saW5nPTE1CiAgI0FkdWx0TW9ydGFsaXR5PTE5OApLYXpha2hzdGFuX1dFSSA8LSAuMDE4NiArIC4wNzE3ICsgLjAyODIqMTUgKyAuMDAwMioxOTgKI05ldyBaZWFsYW5kICAKICAjSERHPVZlcnkgaGlnaAogICNTY2hvb2xpbmc9MTkuMgogICNBZHVsdE1vcnRhbGl0eT02NgpOWl9XRUkgPC0gLjAxODYgKyAuMDcxNyArIC4wMjgyKjE5LjIgKyAuMDAwMio2NgojU3VkYW4KICAjSERHPUxvdwogICNTY2hvb2xpbmc9Ny4yCiAgI0FkdWx0TW9ydGFsaXR5PTIyNQpTdWRhbl9XRUkgPC0gLjAxODYgLSAuMDc0OSArIC4wMjgyKjcuMiArIC4wMDAyKjIyNQojSGFpdGkKICAjSERHPU1lZGl1bQogICNTY2hvb2xpbmc9OS4xCiAgI0FkdWx0TW9ydGFsaXR5PTI0CkhhaXRpX1dFSSA8LSAuMDE4NiAtIC4wMjAyICsgLjAyODIqOS4xICsgLjAwMDIqMjQKI1VrcmFpbmUKICAjSERHPUhpZ2gKICAjU2Nob29saW5nPTE1LjMKICAjQWR1bHRNb3J0YWxpdHk9MTk1ClVrcmFpbmVfV0VJIDwtIC4wMTg2ICsgLjAyODIqMTUuMyArIC4wMDAyKjE5NQpgYGAKCkthemFraHN0YW4gaW1wdXRlZC9wcmVkaWN0ZWQgV0VJIHNjb3JlID0gLjU1MwpOZXcgWmVhbGFuZCBpbXB1dGVkL3ByZWRpY3RlZCBXRUkgc2NvcmUgPSAuNjQ1ClN1ZGFuIGltcHV0ZWQvcHJlZGljdGVkIFdFSSBzY29yZSA9IC4xOTIKSGFpdGkgaW1wdXRlZC9wcmVkaWN0ZWQgV0VJIHNjb3JlID0gLjI2MApVa3JhaW5lIGltcHV0ZWQvcHJlZGljdGVkIFdFSSBzY29yZSA9IC40ODkKClRoZXNlIGRvbid0IGZlZWwgZW50aXJlbHkgYWNjdXJhdGUuIEZvciBleGFtcGxlLCBTdWRhbidzIGltcHV0ZWQgV0VJIHNjb3JlIGlzIGp1c3QgYWJvdmUgdGhhdCBvZiBZZW1lbidzLCB3aGljaCBpcyB0aGUgbG93ZXN0IGluIHRoZSB3b3JsZC4gSG93ZXZlciwgaXQgc3RpbGwgY2FuIGJlIGJlbmVmaWNpYWwgdG8gc2VlIHdoZXJlIHRoZXJlIG1heSBiZSBwb3RlbnRpYWwgZmxhd3MgaW4gdXNpbmcgdGhpcyBsaW5lYXIgbW9kZWwgdG8gcHJlZGljdCBXRUkgc2NvcmVzISBXRUkgc2NvcmUgZGF0YSBvbiAqYWxsKiBjb3VudHJpZXMgaXMgbm90IHB1YmxpYywgc28gSSBhbSB1bmZvcnR1bmF0ZWx5IHVuYWJsZSB0byBjaGVjayB0aGUgYWN0dWFsIGFjY3VyYWN5IG9mIHRoZXNlIHByZWRpY3Rpb25zLiAK